#include "DGL3ProgramEnums.inc"
#include "DGL3Common.glsl"
#include "DGL3ClipUniform.glsl"

//-----------------------------------------

#ifdef VERTEX_SHADER

	#include "DGL3Clip.glsl"
	
	uniform vec4 uColor;
	uniform bool uIsColorPerVertex;
	uniform int uCurveFilteringSamples;


	layout(location = GE_VA_Position) in vec3 aPosition;
	layout(location = GE_VA_Color) in vec3 aColor;
	layout(location = GE_VA_Transform) in ivec2 aAssigns;
	layout(location = GE_VA_FaceIndex) in int aFaceIndex;
	layout(location = GE_VA_EdgeAdjFaces) in ivec2 aEdgeAdj;

	out vec4 vColor;
	flat out int vFaceIndex;
	flat out ivec2 vAdjIndex;

    void main(void)
	{
		vec4 worldPosition = GetModelMatrix(aAssigns.x) * vec4(aPosition, 1.0f);
		gl_Position = uProjectionMatrix * uViewMatrix * worldPosition;
		if (uCurveFilteringSamples > 0)
			gl_Position.z = (gl_Position.z + gl_Position.w) * 0.9995 - gl_Position.w;

		if (uIsColorPerVertex)
			vColor = vec4(aColor, 1.0f);
		else
			vColor = uColor;

		if (uNumClipPlanes > 0)
			SetClipDistance(0, dot(worldPosition, uClipPlane_0));			 
		if (uNumClipPlanes > 1)
			SetClipDistance(1, dot(worldPosition, uClipPlane_1));			 
		if (uNumClipPlanes > 2)
			SetClipDistance(2, dot(worldPosition, uClipPlane_2));			 
		if (uNumClipPlanes > 3)
			SetClipDistance(3, dot(worldPosition, uClipPlane_3));			 
		if (uNumClipPlanes > 4)
			SetClipDistance(4, dot(worldPosition, uClipPlane_4));			 
		if (uNumClipPlanes > 5)
			SetClipDistance(5, dot(worldPosition, uClipPlane_5));	
		vAdjIndex = aEdgeAdj;
		vFaceIndex = aFaceIndex;
	}

#endif

//-----------------------------------------

#ifdef FRAGMENT_SHADER

	in vec4 vColor;
	flat in int vFaceIndex;
	flat in ivec2 vAdjIndex;
	uniform int uEdgeFilteringSamples;
	uniform int uFaceIdTexWidth, uFaceIdTexHeight;
	uniform int uFaceIdOffset;
	uniform int uWriteFaceIds;
	uniform int uCurveFilteringSamples;

	#ifdef efMultiSample
	uniform isampler2DMS faceIdTex;
	uniform sampler2DMS externalDepthTex;
	#endif

	#ifdef efSingleSample
	uniform isampler2D faceIdTex;
	uniform sampler2D externalDepthTex;
	#endif

	layout(location = 0) out vec4 fragColor;
#ifdef OIT
	layout(location = 1) out vec4 fragCount;
#endif
	layout(location = 2) out int faceIndexOut;
	layout(location = 3) out float externalDepth;
	
	void main(void)
	{
		const float dOffsetMult = 0.9999;
		float alpha = vColor.a;

		if (uCurveFilteringSamples > 0)
		{
			int x = int(gl_FragCoord.x);
			int y = int(gl_FragCoord.y);
			float cDepth = 0;
			for (int s = 0; s < uCurveFilteringSamples; ++s)
				cDepth = max(cDepth, texelFetch(externalDepthTex, ivec2(x, y), s).r);
			if (cDepth < gl_FragCoord.z) discard;
		}

		if (uEdgeFilteringSamples > 0)
		{
			bool ok = false;

			int x = int(gl_FragCoord.x);
			int y = int(gl_FragCoord.y);

			float cDepth = 0;

			for (int s = 0; s < uEdgeFilteringSamples; ++s)
				cDepth = max(cDepth, texelFetch(externalDepthTex, ivec2(x, y), s).r);

			if (cDepth < gl_FragCoord.z * dOffsetMult) discard;

			int count = 0;

			for (int s = 0; s < uEdgeFilteringSamples; ++s)
			{
				
				const int idc = texelFetch(faceIdTex, ivec2(x, y), s).r;
				const ivec2 coordl = ivec2(max(0, x - 1), y);
				const ivec2 coordr = ivec2(min(uFaceIdTexWidth - 1, x + 1), y);
				const ivec2 coordb = ivec2(x, max(0, y - 1));
				const ivec2 coordt = ivec2(x, min(uFaceIdTexHeight - 1, y + 1));

				const int idl = texelFetch(faceIdTex, coordl, s).r;
				const int idr = texelFetch(faceIdTex, coordr, s).r;
				const int idt = texelFetch(faceIdTex, coordt, s).r;
				const int idb = texelFetch(faceIdTex, coordb, s).r;

				const int c1 = vAdjIndex.x + uFaceIdOffset;
				const int c2 = vAdjIndex.y + uFaceIdOffset;

				const int aidc = abs(idc);
				const int aidl = abs(idl);
				const int aidr = abs(idr);
				const int aidb = abs(idb);
				const int aidt = abs(idt);


				if ((idc != idl || idc != idr || idc != idb || idc != idt))
				if ((aidc == c1 || aidc == c2) ||
				((aidl == c1 || aidl == c2 || aidt == c1 || aidt == c2 || aidb == c1 || aidb == c2 || aidr == c1 || aidr == c2) &&
				(cDepth >= gl_FragCoord.z)))
				{
					count++;
				}
			}
			if (count == 0) discard;
			alpha = max(0.1, float(count) / uEdgeFilteringSamples);
		}

		fragColor = vColor;
		fragColor.a = alpha;
#ifdef OIT
		fragCount = vec4(1);
#endif
		if (uWriteFaceIds != 0)
		{
			if (gl_FrontFacing)
				faceIndexOut = vFaceIndex + uFaceIdOffset;
			else
				faceIndexOut = -(vFaceIndex + uFaceIdOffset);
			externalDepth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
		}
	}

#endif
